home *** CD-ROM | disk | FTP | other *** search
/ Macwelt 4 / Macwelt DVD 4.cdr / Entwickler / Mac-OS X / Pantomime / Source / IMAPFolder.m / IMAPFolder.m
Encoding:
Text File  |  2002-08-20  |  37.0 KB  |  1,530 lines

  1. /*
  2. **  IMAPFolder.m
  3. **
  4. **  Copyright (c) 2001, 2002
  5. **
  6. **  Author: Ludovic Marcotte <ludovic@Sophos.ca>
  7. **          Anthony W. Juckel <awj@digitalgreen.com>
  8. **
  9. **  This library is free software; you can redistribute it and/or
  10. **  modify it under the terms of the GNU Lesser General Public
  11. **  License as published by the Free Software Foundation; either
  12. **  version 2.1 of the License, or (at your option) any later version.
  13. **  
  14. **  This library is distributed in the hope that it will be useful,
  15. **  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. **  Lesser General Public License for more details.
  18. **  
  19. **  You should have received a copy of the GNU Lesser General Public
  20. **  License along with this library; if not, write to the Free Software
  21. **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  22. */
  23.  
  24. #import <Pantomime/IMAPFolder.h>
  25.  
  26. #import <Pantomime/Connection.h>
  27. #import <Pantomime/Constants.h>
  28. #import <Pantomime/Flags.h>
  29. #import <Pantomime/IMAPCacheManager.h>
  30. #import <Pantomime/IMAPCacheObject.h>
  31. #import <Pantomime/IMAPStore.h>
  32. #import <Pantomime/IMAPMessage.h>
  33. #import <Pantomime/TCPConnection.h>
  34. #import <Pantomime/NSDataExtensions.h>
  35.  
  36. @implementation IMAPFolder
  37.  
  38. //
  39. //
  40. //
  41. - (id) initWithName: (NSString *) theName
  42. {
  43.   [super initWithName: theName];
  44.   
  45.   [self setSelected: YES];
  46.   [self setDelegate: nil];
  47.  
  48.   return self;
  49. }
  50.  
  51.  
  52. //
  53. //
  54. //
  55. - (void) dealloc
  56. {
  57.   RELEASE(imapCacheManager);
  58.   
  59.   [super dealloc];
  60. }
  61.  
  62.  
  63. //
  64. //
  65. //
  66. - (void) appendMessageFromRawSource: (NSData *) theData
  67. {
  68.   [self appendMessageFromRawSource: theData
  69.     flags: nil];
  70. }
  71.  
  72.  
  73. //
  74. //
  75. //
  76. - (void) appendMessageFromRawSource: (NSData *) theData
  77.                               flags: (Flags *) theFlags
  78. {
  79.   NSString *aString, *flagsAsString;
  80.   BOOL newMessagesHaveArrived;
  81.  
  82.   IMAPStore *aStore;
  83.   NSData *aData;
  84.   
  85.   newMessagesHaveArrived = NO;
  86.  
  87.   // We always NOOP before doing anything
  88.   [self noop];
  89.  
  90.   if ( theFlags )
  91.     {
  92.       flagsAsString = [self _flagsAsStringFromFlags: theFlags];
  93.     }
  94.   else
  95.     {
  96.       flagsAsString = @"";
  97.     }
  98.   
  99.   // We remove any invalid headers from our message
  100.   aData = [self _removeInvalidHeadersFromMessage: theData];
  101.  
  102.   // We obtain the pointer to our store
  103.   aStore = (IMAPStore *)[self store];
  104.  
  105.   // We write our IMAP command
  106.   [[aStore tcpConnection] writeLine: [NSString stringWithFormat: @"%@ APPEND \"%@\" (%@) {%d}",
  107.                            [aStore nextTag],  // tag
  108.                            [self name],       // folder name
  109.                            flagsAsString,     // flags
  110.                            [aData length]]];  // length of message in bytes
  111.   
  112.   aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  113.   
  114.   if ( [aString hasPrefix: @"+"] )
  115.     {
  116.       // We write our Message
  117.       [[aStore tcpConnection] writeData: aData];
  118.       
  119.       // We send an empty line (just \r\n)
  120.       [[aStore tcpConnection] writeLine: @""];
  121.       
  122.       // We read the response from our IMAP server
  123.       aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  124.       
  125.       while ( ![aString hasPrefix: [NSString stringWithFormat: @"%@ OK", [aStore lastTag]]] )
  126.     {
  127.       if ( [aString hasCaseInsensitiveSuffix: @"EXISTS"] )
  128.         {
  129.           int aCount;
  130.           
  131.           aCount = [aStore parseExists: aString];
  132.           
  133.           if (aCount > [self count])
  134.         {
  135.           newMessagesHaveArrived = YES;
  136.         }
  137.         }
  138.       else if ( [aString hasPrefix: [NSString stringWithFormat: @"%@ NO", [aStore lastTag]]] ||
  139.             [aString hasPrefix: [NSString stringWithFormat: @"%@ BAD", [aStore lastTag]]] )
  140.         {
  141.           NSException *anException;
  142.           
  143.           NSDebugLog(@"IMAPFolder: APPEND failed %@", aString);
  144.           
  145.           anException = [NSException exceptionWithName: @"PantomimeFolderAppendMessageException"
  146.                      reason: aString
  147.                      userInfo: nil];
  148.           [anException raise];
  149.         }
  150.  
  151.       aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  152.     }
  153.  
  154.       while ( ![aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"%@ OK",
  155.                                 [aStore lastTag]] ])
  156.     {
  157.       if ( [aString hasCaseInsensitiveSuffix: @"EXISTS"] )
  158.         {
  159.           int aCount;
  160.           
  161.           aCount = [aStore parseExists: aString];
  162.           
  163.           //NSDebugLog(@"IMAPFolder: new count = %d, previous = %d", aCount, [self count]);
  164.  
  165.           if (aCount > [self count])
  166.         {
  167.           newMessagesHaveArrived = YES;
  168.         }
  169.         }
  170.       
  171.       // We just read the lines until the end
  172.       aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  173.     }
  174.     }
  175.   else
  176.     {
  177.       NSException *anException;
  178.       
  179.       NSDebugLog(@"IMAPFolder: APPEND failed %@", aString);
  180.       
  181.       anException = [NSException exceptionWithName: @"PantomimeFolderAppendMessageException"
  182.                  reason: aString
  183.                  userInfo: nil];
  184.       [anException raise];
  185.     }
  186.  
  187.   if ( newMessagesHaveArrived )
  188.     {
  189.       [self _newMessagesHaveArrived];
  190.     }
  191. }
  192.  
  193. //
  194. //
  195. //
  196. - (void) deleteMessageWithUID: (int) theUID
  197. {
  198.   Flags *theFlags;
  199.  
  200.   theFlags = [[Flags alloc] init];
  201.   AUTORELEASE(theFlags);
  202.  
  203.   [theFlags add: DELETED];
  204.   
  205.   [self setMessageFlags: theFlags
  206.     forUID: theUID];
  207. }
  208.  
  209.  
  210. //
  211. // This method is used to fetch a complete message for the specified uid
  212. // from the current folder.
  213. //
  214. - (NSData *) prefetchMessageWithUID: (int) theUID
  215. {
  216.   IMAPStore *aStore;
  217.   NSString *aString;
  218.   int msn;
  219.   
  220.   // We obtain the pointer to our store
  221.   aStore = (IMAPStore *)[self store];
  222.  
  223.   // We first ask for the msn of the message
  224.   msn = [self fetchMessageMSNWithUID: theUID];
  225.  
  226.   // We ask for the body of the message
  227.   [[aStore tcpConnection] writeLine: [NSString stringWithFormat: @"%@ UID FETCH %d:%d RFC822",
  228.                            [aStore nextTag], theUID, theUID]];
  229.   
  230.   aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  231.   
  232.   //  if ( [aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"* %d FETCH (UID %d RFC822",
  233.   if ( [aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"* %d FETCH (",
  234.                             msn]] )
  235.     {
  236.       NSMutableData *aMessage;
  237.       
  238.       aMessage = [[NSMutableData alloc] initWithData: [[aStore tcpConnection]
  239.                                 readDataOfLength:
  240.                                   [self parseMessageSegmentSizeFromString:
  241.                                       aString]] ];
  242.       
  243.       // We must replace all occurence of \r\n by \n in the message.
  244.       [self _replaceCRLFInMutableData: aMessage];
  245.  
  246.       // The server response following our RFC822 can be:
  247.       //                             )
  248.       //                             0003 OK UID FETCH
  249.       // OR
  250.       //                             )
  251.       //                             0003 OK Completed
  252.       //
  253.       // We just read those two lines and discard them.
  254.       [[aStore tcpConnection] readLineBySkippingCR: YES];
  255.       [[aStore tcpConnection] readLineBySkippingCR: YES];
  256.  
  257.       return AUTORELEASE(aMessage);
  258.     }
  259.   
  260.   return nil;
  261. }
  262.  
  263.  
  264. //
  265. // This method is used to fetch the message body for the specified uid.
  266. //
  267. - (NSData *) prefetchMessageBodyWithUID: (int) theUID
  268. {
  269.   IMAPStore *aStore;
  270.   NSString *aString;
  271.   int msn;
  272.  
  273.   // We always NOOP before doing anything
  274.   [self noop];
  275.  
  276.   // We obtain the pointer to our store
  277.   aStore = (IMAPStore *)[self store];
  278.  
  279.   // We first ask for the msn of the message
  280.   msn = [self fetchMessageMSNWithUID: theUID];
  281.  
  282.   // We ask for the body of the message
  283.   [[aStore tcpConnection] writeLine: [NSString stringWithFormat: @"%@ UID FETCH %d:%d BODY[TEXT]",
  284.                            [aStore nextTag], theUID, theUID]];
  285.  
  286.   aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  287.   
  288.   // if ( [aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"* %d FETCH (UID %d BODY[TEXT]",
  289.   if ( [aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"* %d FETCH (",
  290.                             msn]] )
  291.     {
  292.       NSMutableData *aBody;
  293.  
  294.       aBody = [[NSMutableData alloc] initWithData:
  295.                      [[aStore tcpConnection] 
  296.                        readDataOfLength:
  297.                          [self parseMessageSegmentSizeFromString: aString]] ];
  298.       
  299.       // We must replace all occurence of \r\n by \n in the body of the message.
  300.       [self _replaceCRLFInMutableData: aBody];
  301.       
  302.       
  303.       // The server response following our BODY[TEXT] can be:
  304.       //                             )
  305.       //                             0003 OK UID FETCH
  306.       // OR
  307.       //                             )
  308.       //                             0003 OK Completed
  309.       //
  310.       // We just read those two lines and discard them.
  311.       [[aStore tcpConnection] readLineBySkippingCR: YES]; // )
  312.       [[aStore tcpConnection] readLineBySkippingCR: YES]; // 0003 OK ...
  313.       
  314.       return AUTORELEASE(aBody);
  315.     }
  316.  
  317.   NSDebugLog(@"IMAPFolder: Returning nil in IMAPFolder: -prefetchMessageBodyWithUID...");
  318.   return nil;
  319. }
  320.  
  321. //
  322. // This method is used to fetch a message headers for the specified uid.
  323. //
  324. - (NSData *) prefetchMessageHeadersWithUID: (int) theUID
  325. {
  326.   IMAPStore *aStore;
  327.   NSString *aString;
  328.   int msn;
  329.  
  330.   // We obtain the pointer to our store
  331.   aStore = (IMAPStore *)[self store];
  332.  
  333.   // We first ask for the msn of the message
  334.   msn = [self fetchMessageMSNWithUID: theUID];
  335.  
  336.   // We ask for the headers of the message
  337.   [[aStore tcpConnection] writeLine: [NSString stringWithFormat: @"%@ UID FETCH %d:%d BODY[HEADER]",
  338.                            [aStore nextTag], theUID, theUID]];
  339.   
  340.   aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  341.  
  342.   //NSDebugLog(@"prefetchMessageHeadersWithUID aString = |%@|", aString);
  343.   
  344.   //
  345.   // The response from the server is like that: * 2 FETCH (UID 873 BODY[HEADER] {1391}
  346.   //                                            * 8 FETCH (FLAGS (\Recent \Seen) UID 9 BODY[HEADER] {2131}
  347.   //             
  348.   if ( [aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"* %d FETCH (", msn]] )
  349.     {
  350.       NSMutableData *aMutableData;
  351.       
  352.       aMutableData = [[NSMutableData alloc] init];
  353.  
  354.       aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  355.       
  356.       //
  357.       // We can receive from the server: 0003 OK Completed
  358.       //                                 0003 OK UID FETCH Completed                                  
  359.       //                                  
  360.       while ( ![aString hasCaseInsensitivePrefix: [NSString stringWithFormat:@"%@ OK",
  361.                                 [aStore lastTag]] ] )
  362.     {
  363.       // We don't append IMAP's extra stuff like:
  364.       // )
  365.       // (empty line)
  366.       if ( [aString length] == 1 && [aString isEqualToString: @")"] )
  367.         {
  368.           aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  369.         }
  370.       else if ([aString length] == 0)
  371.         {
  372.           aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  373.         }
  374.       //else if ( [aString hasCaseInsensitivePrefix: [NSString stringWithFormat:@"* %d FETCH", msn]] )
  375.       //  {
  376.       //   aString = [aStore lineFromSocket];
  377.       //  }
  378.       else
  379.         {
  380.           // FIXME: Everything is legal here? (cString call..)
  381.           [aMutableData appendCString: [aString cString]];
  382.           [aMutableData appendCString: "\n"];
  383.           aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  384.         }
  385.     }
  386.       
  387.       return AUTORELEASE(aMutableData);
  388.     }
  389.   
  390.   NSDebugLog(@"IMAPFolder: Returning nil in IMAPFolder: -prefetchMessageHeadersWithUID...");
  391.   return nil;
  392. }
  393.  
  394.  
  395. //
  396. // This method is used to cache the messages from the POP3 server
  397. // locally (in memory).
  398. //
  399. - (BOOL) prefetch
  400. {
  401.   BOOL didTransferMessages;
  402.   IMAPMessage *aMessage;
  403.   Flags *theFlags;
  404.   int i, last_uid;
  405.   
  406.   // For our cache
  407.   IMAPCacheObject *anObject;
  408.  
  409.   didTransferMessages = NO;
  410.   last_uid = 0;
  411.  
  412.   // We first init from our cache if we need too
  413.   if ( [self imapCacheManager] )
  414.     {
  415.       NSArray *imapCacheObjects;
  416.       NSDictionary *serverFlags;
  417.  
  418.       imapCacheObjects = [[self imapCacheManager] imapCacheObjects];
  419.       
  420.       if ( [imapCacheObjects count] > 0 ) 
  421.     {
  422.       NSAutoreleasePool *pool;
  423.       NSMutableArray *cacheObjectsToDelete;
  424.  
  425.       // We must refetch the flags in case they have changed
  426.       // This is done in bulk, for all messages in the cache
  427.       serverFlags = [self prefetchMessageFlagsFromUID: [[imapCacheObjects objectAtIndex: 0] uid]
  428.                   toUID: [[imapCacheObjects lastObject] uid] ];
  429.       RETAIN(serverFlags);
  430.       
  431.       
  432.       pool = [[NSAutoreleasePool alloc] init];
  433.       cacheObjectsToDelete = [[NSMutableArray alloc] init];
  434.       
  435.       for (i = 0; i < [imapCacheObjects count]; i++)
  436.         {
  437.           Flags *previousFlags;
  438.  
  439.           if ( (i % 100) == 0 )
  440.         {
  441.           TEST_RELEASE(pool);
  442.           pool = [[NSAutoreleasePool alloc] init];
  443.         } 
  444.           
  445.           anObject = [imapCacheObjects objectAtIndex: i]; 
  446.          
  447.           theFlags = (Flags *)[serverFlags objectForKey: [NSNumber numberWithInt: [anObject uid]]];
  448.           
  449.           // If serverFlags does not contain theFlags for a particular message UID,
  450.           // that message must have been deleted from the server. We just go
  451.           // at the end of the loop (ie., we don't initialize a new message)
  452.           if ( theFlags == nil )
  453.         {
  454.           [cacheObjectsToDelete addObject: anObject];
  455.           continue;
  456.         }
  457.  
  458.           aMessage = (IMAPMessage *)[anObject message];
  459.           
  460.           [aMessage setInitialized: NO];
  461.           [aMessage setFolder: self];
  462.           [aMessage setMessageNumber: [anObject uid]];
  463.  
  464.           // FIXME - See the RFC to know what to do about cache coherency for deleted mails
  465.           // We now set our new flags
  466.           previousFlags = [aMessage flags];
  467.           RETAIN(previousFlags);
  468.  
  469.           [aMessage setFlags: theFlags];
  470.  
  471.           // We verify we must add any extra flag we had before
  472.           if ( [previousFlags contain: DELETED] )
  473.         {
  474.           [[aMessage flags] add: DELETED];
  475.         }
  476.  
  477.           if ( [previousFlags contain: TRANSFERRED] )
  478.         {
  479.           [[aMessage flags] add: TRANSFERRED];
  480.         }
  481.           
  482.           // FIXME - That one should come from the IMAP server, not
  483.           //         from the cache but, it is NOT saved right now
  484.           //         on the IMAP server.
  485.           if ( [previousFlags contain: ANSWERED] )
  486.         {
  487.           [[aMessage flags] add: ANSWERED];
  488.         }
  489.           
  490.  
  491.           RELEASE(previousFlags);
  492.           
  493.           [self appendMessage: aMessage];
  494.  
  495.           // We set the last UID
  496.           last_uid = [anObject uid];
  497.         
  498.         } // for (...)
  499.  
  500.       // Let's remove from our cache the messages that have been deleted from
  501.       // the IMAP server
  502.       for (i = 0; i < [cacheObjectsToDelete count]; i++)
  503.         {
  504.           [[self imapCacheManager] removeObject: [cacheObjectsToDelete objectAtIndex: i]];
  505.         }
  506.       RELEASE(cacheObjectsToDelete);
  507.  
  508.       RELEASE(pool);
  509.  
  510.       RELEASE(serverFlags);
  511.     }
  512.     } 
  513.  
  514.   didTransferMessages = [self prefetchNewMessagesStartingAtUID: (last_uid + 1)];
  515.   NSDebugLog(@"IMAPFolder: Done prefetching messages");
  516.   return didTransferMessages;
  517. }
  518.  
  519. //
  520. //
  521. //
  522. - (BOOL) prefetchNewMessagesStartingAtUID: (int) theUID
  523. {
  524.   BOOL didTransferMessages;
  525.   IMAPMessage *aMessage;
  526.   IMAPStore *aStore;
  527.   Flags *theFlags;
  528.   
  529.   NSString *aString;
  530.   NSMutableData *aHeader;
  531.   int uid, size, msn;
  532.  
  533.   // For our cache
  534.   IMAPCacheObject *anObject;
  535.  
  536.   didTransferMessages = NO;
  537.   // Request summary information about all messages
  538.   aStore = (IMAPStore *)[self store];
  539.  
  540.   [[aStore tcpConnection] writeLine: [NSString stringWithFormat: @"%@ UID FETCH %d:* (FLAGS RFC822.SIZE BODY.PEEK[HEADER])", 
  541.                            [aStore nextTag], theUID] ];
  542.   
  543.   aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  544.  
  545.   while ( [aString hasCaseInsensitivePrefix: @"* "] )
  546.     {
  547.       NSAutoreleasePool *pool;
  548.      
  549.       pool = [[NSAutoreleasePool alloc] init];
  550.  
  551.       // First, we read the MSN;
  552.       msn = [self parseMessageMSNFromString: [aString substringFromIndex: 2]];
  553.       
  554.       // Now, check if the UID is included on the first line
  555.       uid = [self parseMessageUIDFromString: aString];
  556.       
  557.       // Then, we read the flags;
  558.       theFlags = [self parseMessageFlagsFromString: aString];
  559.       
  560.       // Then, we read the size;      
  561.       size = [self parseMessageSizeFromString: aString];
  562.       
  563.       // Then, we read the header;
  564.       aHeader = [[NSMutableData alloc] initWithData:
  565.                        [[aStore tcpConnection] readDataOfLength:
  566.                                      [self parseMessageSegmentSizeFromString: aString]] ];
  567.       
  568.       // We must replace all occurence of \r\n by \n in the headers of the message.
  569.       [self _replaceCRLFInMutableData: aHeader];
  570.  
  571.       aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  572.       
  573.       // Finally, we may need to read the UID (which may be at the end of the string);
  574.       if ( !uid )
  575.     {
  576.       uid = [self parseMessageUIDFromString: aString];
  577.     }
  578.       
  579.       // Most IMAP server will *always* return the LAST UID even if we are prefetching *from*
  580.       // that UID. We must just skip it in case that happens.
  581.       if ( uid > (theUID - 1) )
  582.     {
  583.       // The string read is valid, we can now add the message to our folder;
  584.       aMessage = [[IMAPMessage alloc] initWithHeadersFromData: aHeader];
  585.  
  586.       // We set some initial properties to our message;
  587.       [aMessage setInitialized: NO];
  588.       [aMessage setFolder: self];
  589.       [aMessage setMessageNumber: uid];
  590.       [aMessage setFlags: theFlags];
  591.       [aMessage setSize: size];
  592.       
  593.       // We create our cached object;
  594.       anObject = [[IMAPCacheObject alloc] initWithUID: uid
  595.                           message: aMessage];
  596.       
  597.       // NSDebugLog(@"IMAPFolder: Adding new object with UID: %d", uid);
  598.       [self appendMessage: aMessage];
  599.       RELEASE(aMessage);
  600.       
  601.       didTransferMessages = YES;
  602.  
  603.       // We add our new entry to our cache;
  604.       if ( [self imapCacheManager] )
  605.         {
  606.           [[self imapCacheManager] addObject: anObject];
  607.         }
  608.       
  609.       RELEASE(anObject);
  610.     }
  611.  
  612.       RELEASE(pool);
  613.  
  614.       // Finally, we read the next line from the IMAP server;
  615.       aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  616.       
  617.     } /* while */
  618.  
  619.   // We synchronize our cache
  620.   if ( [self imapCacheManager] )
  621.     {
  622.       [[self imapCacheManager] synchronize];
  623.     }
  624.  
  625.   return didTransferMessages;
  626. }
  627.  
  628. //
  629. //
  630. //
  631. - (NSDictionary *) prefetchMessageFlagsFromUID: (int) startUID
  632.                      toUID: (int) endUID
  633. {
  634.   NSMutableDictionary *serverFlags;
  635.   NSAutoreleasePool *pool;
  636.   IMAPStore *aStore;
  637.   Flags *theFlags;
  638.   NSString *aString;
  639.   int uid;
  640.  
  641.   serverFlags = [NSMutableDictionary dictionaryWithCapacity: 100];
  642.  
  643.   aStore = (IMAPStore *)[self store];
  644.  
  645.   // We send our IMAP command
  646.   [[aStore tcpConnection] writeLine: [NSString stringWithFormat: @"%@ UID FETCH %d:%d (FLAGS)", [aStore nextTag], startUID, endUID]];
  647.   aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  648.  
  649.   pool = [[NSAutoreleasePool alloc] init];
  650.   
  651.   while ( [aString hasCaseInsensitivePrefix: @"* "] )
  652.     {
  653.       uid = [self parseMessageUIDFromString: aString];
  654.       theFlags = [self parseMessageFlagsFromString: aString];
  655.       
  656.       [serverFlags setObject: theFlags
  657.            forKey: [NSNumber numberWithInt:uid]];
  658.   
  659.       aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  660.     }
  661.   
  662.   RELEASE(pool);
  663.   
  664.   return serverFlags;
  665. }
  666.  
  667.  
  668. //
  669. // This method simply close the selected mailbox (ie. folder)
  670. //
  671. - (void) close
  672. {
  673.   IMAPStore *aStore;
  674.   NSString *aString;
  675.  
  676.   if ( ![self selected])
  677.     {
  678.       return;
  679.     }
  680.  
  681.   // We sync our cache manager if we have one
  682.   if ( [self imapCacheManager] )
  683.     {
  684.       NSDebugLog(@"IMAPFolder: Synchronizing the IMAP cache manager...");
  685.       [[self imapCacheManager] synchronize];
  686.     }
  687.  
  688.   // We obtain the pointer to our store
  689.   aStore = (IMAPStore *)[self store];
  690.  
  691.   // We ask for the headers of the message
  692.   [[aStore tcpConnection] writeLine: [NSString stringWithFormat:@"%@ CLOSE", [aStore nextTag]]];
  693.  
  694.   aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  695.   
  696.   //
  697.   // The response from the server can be: 0004 OK Completed
  698.   //                                      0004 OK CLOSE completed
  699.   //
  700.   if (! [aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"%@ OK",
  701.                              [aStore lastTag]] ])
  702.     {
  703.       NSDebugLog(@"IMAPFolder: An error occured while closing the current folder.");
  704.     }
  705.  
  706.   // We remove our current folder from the list of opened folders in the store
  707.   [aStore removeFolderFromOpenedFolders: self];
  708. }
  709.  
  710.  
  711. //
  712. // This method returns all messages that have the flag 'deleted'
  713. // All the returned message ARE IN RAW SOURCE.
  714. //
  715. // This method DOES NOT return a message flagged as TRANSFERRED
  716. //
  717. - (NSArray *) expunge: (BOOL) returnDeletedMessages
  718. {
  719.   NSMutableArray *aMutableArray;
  720.   NSString *aString;
  721.   IMAPStore *aStore;
  722.   int i;
  723.  
  724.   aMutableArray = [[NSMutableArray alloc] init];
  725.  
  726.   for (i = [allMessages count] - 1; i >= 0; i--)
  727.     {
  728.       IMAPMessage *aMessage;
  729.       
  730.       aMessage = (IMAPMessage *)[allMessages objectAtIndex: i];
  731.  
  732.       // If the message has been flagged as DELETED
  733.       if ( [[aMessage flags] contain: DELETED] || 
  734.        [[aMessage flags] contain: TRANSFERRED] )
  735.     {
  736.       // We add it to our array of returned message
  737.       if ( [[aMessage flags] contain: DELETED] &&
  738.            returnDeletedMessages )
  739.         {
  740.           NSData *aData = [aMessage rawSource];
  741.           [aMutableArray addObject: aData];
  742.         }
  743.       
  744.       [self deleteMessageWithUID: [aMessage messageNumber] ];
  745.       [super removeMessage: aMessage];
  746.       
  747.       // We remove its entry in our cache
  748.       if ( [self imapCacheManager] )
  749.         {
  750.           IMAPCacheObject *anObject;
  751.       
  752.           anObject = [[self imapCacheManager] findIMAPCacheObject:  [aMessage messageNumber]];
  753.           [[self imapCacheManager] removeObject: anObject];
  754.         }
  755.     }
  756.     }
  757.  
  758.   // We sync our cache manager if we have one
  759.   if ( [self imapCacheManager] )
  760.     {
  761.       NSDebugLog(@"IMAPFolder: Synchronizing the IMAP cache manager...");
  762.       [[self imapCacheManager] synchronize];
  763.     }
  764.  
  765.   // We obtain the pointer to our store
  766.   aStore = (IMAPStore *)[self store];
  767.  
  768.   // We finally call EXPUNGE on our IMAP server
  769.   [[aStore tcpConnection] writeLine: [NSString stringWithFormat: @"%@ EXPUNGE", 
  770.                            [aStore nextTag]] ];
  771.   
  772.   aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  773.   
  774.   while ( ![aString hasCaseInsensitivePrefix: [NSString stringWithFormat:@"%@ OK",
  775.                             [aStore lastTag]] ] )
  776.     {
  777.       // If we got an error, we stop.
  778.       if ( [aString hasCaseInsensitivePrefix: [NSString stringWithFormat:@"%@ NO", [aStore lastTag]]] ||
  779.        [aString hasCaseInsensitivePrefix: [NSString stringWithFormat:@"%@ NO", [aStore lastTag]]] )
  780.     {
  781.       NSDebugLog(@"IMAPFolder: Error occured in -expunge");
  782.       return [NSArray array];
  783.     }
  784.       
  785.       aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  786.     }
  787.  
  788.   return AUTORELEASE(aMutableArray);
  789. }
  790.  
  791.  
  792. //
  793. //
  794. //
  795. - (Flags *) fetchMessageFlagsWithUID: (int) theUID
  796. {
  797.   IMAPStore *aStore;
  798.   NSString *aString;
  799.   Flags *theFlags;
  800.   int msn;
  801.  
  802.   // We first ask for the msn of the message
  803.   msn = [self fetchMessageMSNWithUID: theUID];
  804.   
  805.   // We initialize our Flags
  806.   theFlags = [[Flags alloc] init];
  807.  
  808.   // We obtain the pointer to our store
  809.   aStore = (IMAPStore *)[self store];
  810.  
  811.  
  812.   // We ask for the body of the message
  813.   [[aStore tcpConnection] writeLine: [NSString stringWithFormat: @"%@ UID FETCH %d:%d FLAGS",
  814.                            [aStore nextTag], theUID, theUID]];
  815.   
  816.   aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  817.   
  818.   //
  819.   // The server response is like: * 1 FETCH (UID 872 FLAGS (\Seen))
  820.   //                              * 1 FETCH (FLAGS (\Seen) UID 1)                        
  821.   //
  822.   if ( [aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"* %d FETCH (", msn]] )
  823.     {
  824.       NSRange aRange;
  825.       
  826.       // We check if the message has the Seen or Recent flag
  827.       aRange = [aString rangeOfString: @"\\Seen" options: NSCaseInsensitiveSearch];
  828.       
  829.       if ( aRange.length > 0 )
  830.     {
  831.       [theFlags add: SEEN];
  832.     }
  833.       else
  834.     {
  835.       [theFlags add: RECENT];
  836.     }
  837.       
  838.       // We check if the message has the Deleted flag
  839.       aRange = [aString rangeOfString: @"\\Deleted" options: NSCaseInsensitiveSearch];
  840.       
  841.       if ( aRange.length > 0 )
  842.     {
  843.       [theFlags add: DELETED];
  844.     }
  845.       
  846.       // We check if the message has the Answered flag
  847.       aRange = [aString rangeOfString: @"\\Answered" options: NSCaseInsensitiveSearch];
  848.       
  849.       if ( aRange.length > 0 )
  850.     {
  851.       [theFlags add: ANSWERED];
  852.     }
  853.       
  854.       // We finally read the extra line on our socket and we discard it.
  855.       [[aStore tcpConnection] readLineBySkippingCR: YES];
  856.     }
  857.  
  858.   return AUTORELEASE(theFlags);
  859. }
  860.  
  861.  
  862. //
  863. //
  864. //
  865. - (int) fetchMessageSizeWithUID: (int) theUID
  866. {
  867.   IMAPStore *aStore;
  868.   NSString *aString;
  869.   int size, msn;
  870.  
  871.   // We first ask for the msn of the message
  872.   msn = [self fetchMessageMSNWithUID: theUID];
  873.  
  874.   size = 0;
  875.  
  876.   // We obtain the pointer to our store
  877.   aStore = (IMAPStore *)[self store];
  878.  
  879.   // We ask for the body of the message
  880.   [[aStore tcpConnection] writeLine: [NSString stringWithFormat: @"%@ UID FETCH %d:%d RFC822.SIZE",
  881.                            [aStore nextTag], theUID, theUID]];
  882.   
  883.   aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  884.  
  885.   // The server response is like: * 1 FETCH (UID 872 RFC822.SIZE 4222)
  886.   // if ( [aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"* %d FETCH (UID %d RFC822.SIZE",
  887.   if ( [aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"* %d FETCH (",
  888.                             msn]] )
  889.     {
  890.       NSRange aRange;
  891.  
  892.       aRange = [aString rangeOfString: @"RFC822.SIZE"];
  893.       
  894.       if (aRange.length > 0)
  895.     {
  896.       int mark;
  897.       
  898.       mark = aRange.location + aRange.length + 1;
  899.       aString = [aString substringWithRange: NSMakeRange(mark, [aString length] - mark - 1)];
  900.       size = [aString intValue];
  901.     }
  902.       
  903.       // We finally read the extra line on our socket and we discard it.
  904.       [[aStore tcpConnection] readLineBySkippingCR: YES];
  905.     }
  906.   
  907.   return size;
  908. }
  909.  
  910.  
  911.  
  912. //
  913. //
  914. //
  915. - (int) fetchMessageMSNWithUID: (int) theUID
  916. {
  917.   IMAPStore *aStore;
  918.   NSString *aString;
  919.  
  920.   // We obtain the pointer to our store
  921.   aStore = (IMAPStore *)[self store];
  922.  
  923.  
  924.   // We ask for the UID of the message
  925.   [[aStore tcpConnection] writeLine: [NSString stringWithFormat: @"%@ UID FETCH %d:%d UID",
  926.                            [aStore nextTag], theUID, theUID]];
  927.   
  928.   aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  929.   
  930.   //
  931.   // The server response is like: * 46 FETCH (UID 960)
  932.   //
  933.   if ( [aString hasCaseInsensitivePrefix: @"* "] )
  934.     {      
  935.       NSRange aRange;
  936.       
  937.       aString = [aString substringFromIndex: 2];
  938.       aRange = [aString rangeOfString: @" "];
  939.       
  940.       if (aRange.length > 0)
  941.     {
  942.       aString = [aString substringWithRange: NSMakeRange(0, aRange.location)];
  943.     }
  944.       
  945.       // We finally read the extra line on our socket and we discard it.
  946.       [[aStore tcpConnection] readLineBySkippingCR: YES];
  947.     }
  948.  
  949.   return [aString intValue];
  950. }
  951.  
  952. //
  953. //
  954. //
  955. - (Flags *) parseMessageFlagsFromString: (NSString *) aString
  956. {
  957.   NSRange aRange;
  958.   Flags *theFlags;
  959.   
  960.   theFlags = [[Flags alloc] init];
  961.  
  962.   // We check if the message has the Seen or Recent flag
  963.   aRange = [aString rangeOfString: @"\\Seen" 
  964.             options: NSCaseInsensitiveSearch];
  965.       
  966.   if ( aRange.length > 0 )
  967.     {
  968.       [theFlags add: SEEN];
  969.     }
  970.   else
  971.     {
  972.       [theFlags add: RECENT];
  973.     }
  974.   
  975.   // We check if the message has the Deleted flag
  976.   aRange = [aString rangeOfString: @"\\Deleted"
  977.             options: NSCaseInsensitiveSearch];
  978.       
  979.   if ( aRange.length > 0 )
  980.     {
  981.       [theFlags add: DELETED];
  982.     }
  983.   
  984.   // We check if the message has the Answered flag
  985.   aRange = [aString rangeOfString: @"\\Answered"
  986.             options: NSCaseInsensitiveSearch];
  987.       
  988.   if ( aRange.length > 0 )
  989.     {
  990.       [theFlags add: ANSWERED];
  991.     }
  992.  
  993.   // We check if the message has the Flagged flag
  994.   aRange = [aString rangeOfString: @"\\Flagged"
  995.             options: NSCaseInsensitiveSearch];
  996.   
  997.   if ( aRange.length > 0 )
  998.     {
  999.       [theFlags add: FLAGGED];
  1000.     }
  1001.  
  1002.   // We check if the message has the Draft flag
  1003.   aRange = [aString rangeOfString: @"\\Draft"
  1004.             options: NSCaseInsensitiveSearch];
  1005.   
  1006.   if ( aRange.length > 0 )
  1007.     {
  1008.       [theFlags add: DRAFT];
  1009.     }
  1010.  
  1011.   return AUTORELEASE(theFlags);
  1012. }
  1013.  
  1014. //
  1015. //
  1016. //
  1017. - (int) parseMessageSizeFromString: (NSString *) aString
  1018. {
  1019.   NSRange aRange;
  1020.  
  1021.   aRange = [aString rangeOfString: @"RFC822.SIZE"];
  1022.  
  1023.   if (aRange.length > 0)
  1024.     {
  1025.       int mark;
  1026.       
  1027.       mark = aRange.location + aRange.length + 1;
  1028.       
  1029.       aRange = [aString rangeOfString: @" " 
  1030.             options: 0 
  1031.             range: NSMakeRange(mark, [aString length] - mark - 1)];
  1032.       
  1033.       return [[aString substringWithRange: NSMakeRange(mark, aRange.location - mark)] intValue];
  1034.     }
  1035.   else
  1036.     {
  1037.       return 0;
  1038.     }
  1039. }
  1040.  
  1041. //
  1042. //
  1043. //
  1044. - (int) parseMessageMSNFromString: (NSString *) aString
  1045. {
  1046.   NSRange aRange;
  1047.  
  1048.   aRange = [aString rangeOfString: @" "];
  1049.  
  1050.   if (aRange.length > 0)
  1051.     {
  1052.       return [[aString substringWithRange: NSMakeRange(0, aRange.location)] intValue];
  1053.     }
  1054.   else
  1055.     {
  1056.       return 0;
  1057.     }
  1058. }
  1059.  
  1060. //
  1061. //
  1062. //
  1063. - (int) parseMessageSegmentSizeFromString: (NSString *) aString
  1064. {
  1065.   NSRange aRange;
  1066.   
  1067.   aRange = [aString rangeOfString: @"{"];
  1068.  
  1069.   if (aRange.length > 0)
  1070.     {
  1071.       int mark;
  1072.       
  1073.       mark = aRange.location + aRange.length;
  1074.       aRange = [aString rangeOfString: @"}"
  1075.             options: 0
  1076.             range: NSMakeRange(mark, [aString length] - mark)];
  1077.       
  1078.       return [[aString substringWithRange: NSMakeRange(mark, aRange.location - mark)] intValue];
  1079.     }
  1080.   else
  1081.     {
  1082.       return 0;
  1083.     }
  1084. }
  1085.  
  1086. //
  1087. //
  1088. //
  1089. - (int) parseMessageUIDFromString: (NSString *) aString
  1090. {
  1091.   NSRange aRange;
  1092.  
  1093.   aRange = [aString rangeOfString: @"UID"];
  1094.   
  1095.   if( aRange.length > 0 )
  1096.     {
  1097.       int mark = aRange.location + aRange.length + 1;
  1098.       aRange = [aString rangeOfString: @" "
  1099.             options: 0
  1100.             range: NSMakeRange(mark, [aString length] - mark)];
  1101.       
  1102.       if( aRange.length > 0 ) 
  1103.     {
  1104.       return [[aString substringWithRange: NSMakeRange(mark, aRange.location - mark)] intValue];
  1105.     } 
  1106.       else
  1107.     {
  1108.       aRange = [aString rangeOfString: @")"
  1109.                 options: 0
  1110.                 range: NSMakeRange(mark, [aString length] - mark)];
  1111.       
  1112.       if( aRange.length > 0 )
  1113.         {
  1114.           return [[aString substringWithRange: NSMakeRange(mark, aRange.location - mark)] intValue];
  1115.         }
  1116.       else
  1117.         {
  1118.           return 0;
  1119.         }
  1120.     }
  1121.     }
  1122.   else
  1123.     {
  1124.       return 0;
  1125.     }
  1126. }
  1127.  
  1128. // 
  1129. // Return an array of NSNumber
  1130. //
  1131. - (NSArray *) uncachedUIDStartingAtUID: (int) theUID
  1132. {
  1133.   NSMutableArray *aMutableArray;
  1134.   IMAPStore *aStore;
  1135.   NSString *aString;
  1136.  
  1137.   NSDebugLog(@"IMAPFolder: Starting search from UID = %d", theUID);
  1138.  
  1139.   // We obtain the pointer to our store
  1140.   aStore = (IMAPStore *)[self store];
  1141.  
  1142.   // We init our return value
  1143.   aMutableArray = [[NSMutableArray alloc] init];
  1144.   
  1145.   // We ask for the list of UIDs
  1146.   [[aStore tcpConnection] writeLine: [NSString stringWithFormat: @"%@ UID FETCH %d:* UID", 
  1147.                            [aStore nextTag], theUID]];
  1148.   
  1149.   aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  1150.  
  1151.   while ( [aString hasCaseInsensitivePrefix: @"* "] )
  1152.     { 
  1153.       NSRange aRange;
  1154.       
  1155.       aRange = [aString rangeOfString: @"FETCH (UID"];
  1156.       
  1157.       if (aRange.length > 0)
  1158.     {
  1159.       aString = [aString substringWithRange: NSMakeRange(aRange.location + aRange.length + 1, 
  1160.                                  [aString length] - (aRange.location + aRange.length) - 2)];
  1161.       
  1162.       //NSDebugLog(@"Adding uid from string = |%@|", aString);
  1163.       [aMutableArray addObject: [NSNumber numberWithInt: [aString intValue]]];
  1164.     }
  1165.  
  1166.       aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  1167.     }
  1168.  
  1169.   return AUTORELEASE(aMutableArray);
  1170. }
  1171.  
  1172.  
  1173. //
  1174. //
  1175. //
  1176. - (void) setMessageFlags: (Flags *) theFlags
  1177.           forUID: (int) theUID
  1178. {
  1179.   IMAPStore *aStore;
  1180.   NSString *aString;
  1181.   NSMutableString *flagString;
  1182.   
  1183.   if (! theFlags )
  1184.     {
  1185.       return;
  1186.     }
  1187.  
  1188.   // We obtain the pointer to our store
  1189.   aStore = (IMAPStore *)[self store];
  1190.   
  1191.   // We create our string of flags
  1192.   flagString = [[NSMutableString alloc] init];
  1193.   
  1194.   if ( [theFlags contain: DELETED] )
  1195.     {
  1196.       [flagString appendString: @"\\Deleted"];
  1197.     }
  1198.  
  1199.  
  1200.   // Set the flag to \Deleted to the message at the specified index
  1201.   [[aStore tcpConnection] writeLine: [NSString stringWithFormat: @"%@ UID STORE %d:%d +FLAGS (%@)",
  1202.                            [aStore nextTag], theUID, theUID, flagString]];
  1203.   
  1204.   aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  1205.   
  1206.   if ( [aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"%@ NO", [aStore lastTag]]] ||
  1207.        [aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"%@ BAD", [aStore lastTag]]] )
  1208.     {
  1209.       NSDebugLog(@"IMAPFolder: An error occured while setting the flags of message the message with UID = %@", theUID);
  1210.     }
  1211.   else
  1212.     {
  1213.       //
  1214.       // The response can be: 0005 OK UID STORE
  1215.       //                      0003 OK Completed
  1216.       //
  1217.       while (! [aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"%@ OK",
  1218.                                 [aStore lastTag]] ])
  1219.     {
  1220.       // We just read the lines until the end
  1221.       aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  1222.     }
  1223.     }
  1224.   
  1225.   RELEASE(flagString);
  1226. }
  1227.  
  1228.  
  1229. //
  1230. //
  1231. //
  1232. - (void) noop
  1233. {
  1234.   IMAPStore *aStore;
  1235.   NSString *aString;
  1236.   BOOL newMessagesHaveArrived;
  1237.  
  1238.   newMessagesHaveArrived = NO;
  1239.   
  1240.   // We obtain the pointer to our store
  1241.   aStore = (IMAPStore *)[self store];
  1242.   
  1243.   // Set the flag to \Deleted to the message at the specified index
  1244.   [[aStore tcpConnection] writeLine: [NSString stringWithFormat: @"%@ NOOP", [aStore nextTag]]];;
  1245.   
  1246.   aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  1247.   
  1248.   //NSDebugLog(@"aString = |%@|", aString);
  1249.  
  1250.   if ( [aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"%@ OK", [aStore lastTag]]] ||
  1251.        [aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"%@ BAD", [aStore lastTag]]] )
  1252.     {
  1253.       return;
  1254.     }
  1255.   else
  1256.     {
  1257.       //
  1258.       // The server response can be: 0004 OK NOOP
  1259.       //                             0004 OK Completed
  1260.       // 
  1261.       while ( ![aString hasCaseInsensitivePrefix: [NSString stringWithFormat: @"%@ OK",
  1262.                                 [aStore lastTag]] ])
  1263.     {
  1264.       if ( [aString hasCaseInsensitiveSuffix: @"EXISTS"] )
  1265.         {
  1266.           int aCount;
  1267.           
  1268.           aCount = [aStore parseExists: aString];
  1269.           
  1270.           //NSDebugLog(@"IMAPFolder: new count = %d, previous = %d", aCount, [self count]);
  1271.  
  1272.           if (aCount > [self count])
  1273.         {
  1274.           newMessagesHaveArrived = YES;
  1275.         }
  1276.         }
  1277.       
  1278.       // We just read the lines until the end
  1279.       aString = [[aStore tcpConnection] readLineBySkippingCR: YES];
  1280.     }
  1281.     }
  1282.  
  1283.   if ( newMessagesHaveArrived )
  1284.     {
  1285.       [self _newMessagesHaveArrived];
  1286.     }
  1287. }
  1288.  
  1289. //
  1290. //
  1291. //
  1292. - (int) uidValidity
  1293. {
  1294.   return uidValidity;
  1295. }
  1296.  
  1297.  
  1298. //
  1299. //
  1300. //
  1301. - (void) setUIDValidity: (int) theUIDValidity
  1302. {
  1303.   NSDebugLog(@"IMAPFolder: UIDVALIDITY = %d", theUIDValidity);
  1304.   uidValidity = theUIDValidity;
  1305. }
  1306.  
  1307.  
  1308. //
  1309. //
  1310. //
  1311. - (IMAPCacheManager *) imapCacheManager
  1312. {
  1313.   return imapCacheManager;
  1314. }
  1315.  
  1316.  
  1317. //
  1318. //
  1319. //
  1320. - (void) setIMAPCacheManager: (IMAPCacheManager *) theIMAPCacheManager
  1321. {
  1322.   if ( theIMAPCacheManager )
  1323.     {
  1324.       RETAIN(theIMAPCacheManager);
  1325.       RELEASE(imapCacheManager);
  1326.       imapCacheManager = theIMAPCacheManager;
  1327.     }
  1328.   else
  1329.     {
  1330.       RELEASE(imapCacheManager);
  1331.       imapCacheManager = nil;
  1332.     }
  1333. }
  1334.  
  1335.  
  1336. //
  1337. //
  1338. //
  1339. - (BOOL) selected
  1340. {
  1341.   return selected;
  1342. }
  1343.  
  1344. - (void) setSelected: (BOOL) aBOOL
  1345. {
  1346.   selected = aBOOL;
  1347. }
  1348.  
  1349. //
  1350. // This method re-implement the super method to allow
  1351. // to set various flags.
  1352. //
  1353. - (void) removeMessage: (Message *) theMessage
  1354. {
  1355.   [[theMessage flags] add: TRANSFERRED];
  1356.   [super removeMessage: theMessage];
  1357. }
  1358.  
  1359.  
  1360. //
  1361. //
  1362. //
  1363. - (id) delegate
  1364. {
  1365.   return delegate;
  1366. }
  1367.  
  1368. - (void) setDelegate: (id) theDelegate
  1369. {
  1370.   if ( theDelegate )
  1371.     {
  1372.       RETAIN(theDelegate);
  1373.       RELEASE(delegate);
  1374.       delegate = theDelegate;
  1375.     }
  1376.   else
  1377.     {
  1378.       DESTROY(delegate);
  1379.     }
  1380. }
  1381.  
  1382. @end
  1383.  
  1384.  
  1385. //
  1386. // Private methods
  1387. // 
  1388. @implementation IMAPFolder (Private)
  1389.  
  1390. //
  1391. //
  1392. //
  1393. - (NSData *) _removeInvalidHeadersFromMessage: (NSData *) theMessage
  1394. {
  1395.   NSMutableData *aMutableData;
  1396.   NSArray *allLines;
  1397.   int i;
  1398.  
  1399.   // We allocate our mutable data object
  1400.   aMutableData = [[NSMutableData alloc] initWithCapacity: [theMessage length]];
  1401.   
  1402.   // We now replace all \n by \r\n
  1403.   allLines = [theMessage componentsSeparatedByCString: "\n"];
  1404.   
  1405.   for (i = 0; i < [allLines count]; i++)
  1406.     {
  1407.       NSData *aLine;
  1408.  
  1409.       // We get a line...
  1410.       aLine = [allLines objectAtIndex: i];
  1411.  
  1412.       // We skip dumb headers
  1413.       if ( [aLine hasCPrefix: "From "] )
  1414.     {
  1415.       continue;
  1416.     }
  1417.  
  1418.       [aMutableData appendData: aLine];
  1419.       [aMutableData appendCString: "\r\n"];
  1420.     }
  1421.  
  1422.   return AUTORELEASE(aMutableData);
  1423. }
  1424.  
  1425.  
  1426. //
  1427. //
  1428. //
  1429. - (void) _replaceCRLFInMutableData: (NSMutableData *) theMutableData
  1430. {
  1431.   unsigned char *bytes, *bi, *bo;
  1432.   int delta, i,length;
  1433.   
  1434.   bytes = [theMutableData mutableBytes];
  1435.   length = [theMutableData length];
  1436.   bi = bo = bytes;
  1437.   
  1438.   for (i = delta = 0; i < length; i++, bi++)
  1439.     {
  1440.       if ( i+1 < length && bi[0] == '\r' && bi[1] == '\n')
  1441.     {
  1442.       i++;
  1443.       bi++;
  1444.       delta++;
  1445.     }
  1446.       
  1447.       *bo = *bi;
  1448.       bo++;
  1449.     }
  1450.   
  1451.   [theMutableData setLength: length-delta];
  1452. }
  1453.  
  1454.  
  1455. //
  1456. //
  1457. //
  1458. - (NSString *) _flagsAsStringFromFlags: (Flags *) theFlags
  1459. {
  1460.   NSMutableString *aMutableString;
  1461.  
  1462.   aMutableString = [[NSMutableString alloc] init];
  1463.   AUTORELEASE(aMutableString);
  1464.  
  1465.   if ( [theFlags contain: ANSWERED] )
  1466.     {
  1467.       [aMutableString appendString: @"\\Answered "];
  1468.     }
  1469.  
  1470.   if ( [theFlags contain: DRAFT] )
  1471.     {
  1472.       [aMutableString appendString: @"\\Draft "];
  1473.     }
  1474.  
  1475.   if ( [theFlags contain: FLAGGED] )
  1476.     {
  1477.       [aMutableString appendString: @"\\Flagged "];
  1478.     }
  1479.  
  1480.   if ( [theFlags contain: SEEN] )
  1481.     {
  1482.       [aMutableString appendString: @"\\Seen "];
  1483.     }
  1484.   
  1485.   if ( [theFlags contain: DELETED] )
  1486.     {
  1487.       [aMutableString appendString: @"\\Deleted "];
  1488.     }
  1489.  
  1490.   return [aMutableString stringByTrimmingWhiteSpaces];
  1491. }
  1492.  
  1493.  
  1494. //
  1495. //
  1496. //
  1497. - (void) _newMessagesHaveArrived
  1498. {
  1499.   SEL aSelector;
  1500.  
  1501.   int last_uid;
  1502.   
  1503.   last_uid = 0;
  1504.       
  1505.   if ( [self imapCacheManager] )
  1506.     {
  1507.       NSArray *imapCacheObjects;
  1508.       IMAPCacheObject *anObject;
  1509.       
  1510.       imapCacheObjects = [[self imapCacheManager] imapCacheObjects];
  1511.       
  1512.       anObject = [imapCacheObjects lastObject];
  1513.       
  1514.       last_uid = [anObject uid];
  1515.     }
  1516.       
  1517.   [self prefetchNewMessagesStartingAtUID: (last_uid + 1)];
  1518.  
  1519.   aSelector = @selector(newMessagesWereReceived:);
  1520.   
  1521.   if ( delegate && [delegate respondsToSelector: aSelector] )
  1522.     {
  1523.       [delegate performSelector: aSelector
  1524.         withObject: self];
  1525.     }
  1526. }
  1527.  
  1528. @end
  1529.  
  1530.